home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / glibc-1.09 / glibc-1 / glibc-1.09.1 / time / mktime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-27  |  13.6 KB  |  503 lines

  1. /* Copyright (C) 1993, 1994 Free Software Foundation, Inc.
  2.    Contributed by Noel Cragg (noel@cs.oberlin.edu), with fixes by
  3.    Michael E. Calwas (calwas@ttd.teradyne.com) and
  4.    Wade Hampton (tasi029@tmn.com).
  5.  
  6. This file is part of the GNU C Library.
  7.  
  8. The GNU C Library is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU Library General Public License as
  10. published by the Free Software Foundation; either version 2 of the
  11. License, or (at your option) any later version.
  12.  
  13. The GNU C Library is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. Library General Public License for more details.
  17.  
  18. You should have received a copy of the GNU Library General Public
  19. License along with the GNU C Library; see the file COPYING.LIB.  If
  20. not, write to the Free Software Foundation, Inc., 675 Mass Ave,
  21. Cambridge, MA 02139, USA.  */
  22.  
  23. /* Define this to have a standalone program to test this implementation of
  24.    mktime.  */
  25. /* #define DEBUG */
  26.  
  27. #ifdef HAVE_CONFIG_H
  28. #include <config.h>
  29. #endif
  30.  
  31. #include <sys/types.h>        /* Some systems define `time_t' here.  */
  32. #include <time.h>
  33.  
  34.  
  35. #ifndef __isleap
  36. /* Nonzero if YEAR is a leap year (every 4 years,
  37.    except every 100th isn't, and every 400th is).  */
  38. #define    __isleap(year)    \
  39.   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
  40. #endif
  41.  
  42. #ifndef __P
  43. #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
  44. #define __P(args) args
  45. #else
  46. #define __P(args) ()
  47. #endif  /* GCC.  */
  48. #endif  /* Not __P.  */
  49.  
  50. /* How many days are in each month.  */
  51. const unsigned short int __mon_lengths[2][12] =
  52.   {
  53.     /* Normal years.  */
  54.     { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  55.     /* Leap years.  */
  56.     { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
  57.   };
  58.  
  59.  
  60. static int times_through_search; /* This library routine should never
  61.                     hang -- make sure we always return
  62.                     when we're searching for a value */
  63.  
  64.  
  65. #ifdef DEBUG
  66.  
  67. #include <stdio.h>
  68. #include <ctype.h>
  69.  
  70. int debugging_enabled = 0;
  71.  
  72. /* Print the values in a `struct tm'. */
  73. static void
  74. printtm (it)
  75.      struct tm *it;
  76. {
  77.   printf ("%02d/%02d/%04d %02d:%02d:%02d (%s) yday:%03d dst:%d gmtoffset:%ld",
  78.       it->tm_mon + 1,
  79.       it->tm_mday,
  80.       it->tm_year + 1900,
  81.       it->tm_hour,
  82.       it->tm_min,
  83.       it->tm_sec,
  84.       it->tm_zone,
  85.       it->tm_yday,
  86.       it->tm_isdst,
  87.       it->tm_gmtoff);
  88. }
  89. #endif
  90.  
  91.  
  92. static time_t
  93. dist_tm (t1, t2)
  94.      struct tm *t1;
  95.      struct tm *t2;
  96. {
  97.   time_t distance = 0;
  98.   unsigned long int v1, v2;
  99.   int diff_flag = 0;
  100.  
  101.   v1 = v2 = 0;
  102.  
  103. #define doit(x, secs)                                                         \
  104.   v1 += t1->x * secs;                                                         \
  105.   v2 += t2->x * secs;                                                         \
  106.   if (!diff_flag)                                                             \
  107.     {                                                                         \
  108.       if (t1->x < t2->x)                                                      \
  109.     diff_flag = -1;                                                       \
  110.       else if (t1->x > t2->x)                                                 \
  111.     diff_flag = 1;                                                        \
  112.     }
  113.   
  114.   doit (tm_year, 31536000);    /* Okay, not all years have 365 days. */
  115.   doit (tm_mon, 2592000);    /* Okay, not all months have 30 days. */
  116.   doit (tm_mday, 86400);
  117.   doit (tm_hour, 3600);
  118.   doit (tm_min, 60);
  119.   doit (tm_sec, 1);
  120.   
  121. #undef doit
  122.   
  123.   /* We should also make sure that the sign of DISTANCE is correct -- if
  124.      DIFF_FLAG is positive, the distance should be positive and vice versa. */
  125.   
  126.   distance = (v1 > v2) ? (v1 - v2) : (v2 - v1);
  127.   if (diff_flag < 0)
  128.     distance = -distance;
  129.  
  130.   if (times_through_search > 20) /* Arbitrary # of calls, but makes sure we
  131.                     never hang if there's a problem with
  132.                     this algorithm.  */
  133.     {
  134.       distance = diff_flag;
  135.     }
  136.  
  137.   /* We need this DIFF_FLAG business because it is forseeable that the
  138.      distance may be zero when, in actuality, the two structures are
  139.      different.  This is usually the case when the dates are 366 days apart
  140.      and one of the years is a leap year.  */
  141.  
  142.   if (distance == 0 && diff_flag)
  143.     distance = 86400 * diff_flag;
  144.  
  145.   return distance;
  146. }
  147.       
  148.  
  149. /* MKTIME converts the values in a struct tm to a time_t.  The values
  150.    in tm_wday and tm_yday are ignored; other values can be put outside
  151.    of legal ranges since they will be normalized.  This routine takes
  152.    care of that normalization. */
  153.  
  154. void
  155. do_normalization (tmptr)
  156.      struct tm *tmptr;
  157. {
  158.  
  159. #define normalize(foo,x,y,bar); \
  160.   while (tmptr->foo < x) \
  161.     { \
  162.       tmptr->bar--; \
  163.       tmptr->foo = (y - (x - tmptr->foo) + 1); \
  164.     } \
  165.   while (tmptr->foo > y) \
  166.     { \
  167.       tmptr->foo = (x + (tmptr->foo - y) - 1); \
  168.       tmptr->bar++; \
  169.     }
  170.   
  171.   normalize (tm_sec, 0, 59, tm_min);
  172.   normalize (tm_min, 0, 59, tm_hour);
  173.   normalize (tm_hour, 0, 23, tm_mday);
  174.   
  175.   /* Do the month first, so day range can be found. */
  176.   normalize (tm_mon, 0, 11, tm_year);
  177.  
  178.   /* Since the day range modifies the month, we should be careful how
  179.      we reference the array of month lengths -- it is possible that
  180.      the month will go negative, hence the modulo...
  181.  
  182.      Also, tm_year is the year - 1900, so we have to 1900 to have it
  183.      work correctly. */
  184.  
  185.   normalize (tm_mday, 1,
  186.          __mon_lengths[__isleap (tmptr->tm_year + 1900)]
  187.                           [((tmptr->tm_mon < 0)
  188.                 ? (12 + (tmptr->tm_mon % 12))
  189.                 : (tmptr->tm_mon % 12)) ],
  190.          tm_mon);
  191.  
  192.   /* Do the month again, because the day may have pushed it out of range. */
  193.   normalize (tm_mon, 0, 11, tm_year);
  194.  
  195.   /* Do the day again, because the month may have changed the range. */
  196.   normalize (tm_mday, 1,
  197.          __mon_lengths[__isleap (tmptr->tm_year + 1900)]
  198.                       [((tmptr->tm_mon < 0)
  199.                 ? (12 + (tmptr->tm_mon % 12))
  200.                 : (tmptr->tm_mon % 12)) ],
  201.          tm_mon);
  202.   
  203. #ifdef DEBUG
  204.   if (debugging_enabled)
  205.     {
  206.       printf ("   After normalizing:\n     ");
  207.       printtm (tmptr);
  208.       putchar ('\n');
  209.     }
  210. #endif
  211.  
  212. }
  213.  
  214.  
  215. /* Here's where the work gets done. */
  216.  
  217. #define BAD_STRUCT_TM ((time_t) -1)
  218.  
  219. time_t
  220. _mktime_internal (timeptr, producer)
  221.      struct tm *timeptr;
  222.      struct tm *(*producer) __P ((const time_t *));
  223. {
  224.   struct tm our_tm;        /* our working space */
  225.   struct tm *me = &our_tm;    /* a pointer to the above */
  226.   time_t result;        /* the value we return */
  227.  
  228.   *me = *timeptr;        /* copy the struct tm that was passed
  229.                    in by the caller */
  230.  
  231.  
  232.   /***************************/
  233.   /* Normalize the structure */
  234.   /***************************/
  235.  
  236.   /* This routine assumes that the value of TM_ISDST is -1, 0, or 1.
  237.      If the user didn't pass it in that way, fix it. */
  238.  
  239.   if (me->tm_isdst > 0)
  240.     me->tm_isdst = 1;
  241.   else if (me->tm_isdst < 0)
  242.     me->tm_isdst = -1;
  243.  
  244.   do_normalization (me);
  245.  
  246.   /* Get out of here if it's not possible to represent this struct.
  247.      If any of the values in the normalized struct tm are negative,
  248.      our algorithms won't work.  Luckily, we only need to check the
  249.      year at this point; normalization guarantees that all values will
  250.      be in correct ranges EXCEPT the year. */
  251.  
  252.   if (me->tm_year < 0)
  253.     return BAD_STRUCT_TM;
  254.  
  255.   /*************************************************/
  256.   /* Find the appropriate time_t for the structure */
  257.   /*************************************************/
  258.  
  259.   /* Modified b-search -- make intelligent guesses as to where the
  260.      time might lie along the timeline, assuming that our target time
  261.      lies a linear distance (w/o considering time jumps of a
  262.      particular region).
  263.  
  264.      Assume that time does not fluctuate at all along the timeline --
  265.      e.g., assume that a day will always take 86400 seconds, etc. --
  266.      and come up with a hypothetical value for the time_t
  267.      representation of the struct tm TARGET, in relation to the guess
  268.      variable -- it should be pretty close!
  269.  
  270.      After testing this, the maximum number of iterations that I had
  271.      on any number that I tried was 3!  Not bad.
  272.  
  273.      The reason this is not a subroutine is that we will modify some
  274.      fields in the struct tm (yday and mday).  I've never felt good
  275.      about side-effects when writing structured code... */
  276.  
  277.   {
  278.     struct tm *guess_tm;
  279.     time_t guess = 0;
  280.     time_t distance = 0;
  281.     time_t last_distance = 0;
  282.  
  283.     times_through_search = 0;
  284.  
  285.     do
  286.       {
  287.     guess += distance;
  288.  
  289.     times_through_search++;     
  290.       
  291.     guess_tm = (*producer) (&guess);
  292.       
  293. #ifdef DEBUG
  294.     if (debugging_enabled)
  295.       {
  296.         printf ("   Guessing time_t == %d\n     ", (int) guess);
  297.         printtm (guess_tm);
  298.         putchar ('\n');
  299.       }
  300. #endif
  301.       
  302.     /* How far is our guess from the desired struct tm? */
  303.     distance = dist_tm (me, guess_tm);
  304.       
  305.     /* Handle periods of time where a period of time is skipped.
  306.        For example, 2:15 3 April 1994 does not exist, because DST
  307.        is in effect.  The distance function will alternately
  308.        return values of 3600 and -3600, because it doesn't know
  309.        that the requested time doesn't exist.  In these situations
  310.        (even if the skip is not exactly an hour) the distances
  311.        returned will be the same, but alternating in sign.  We
  312.        want the later time, so check to see that the distance is
  313.        oscillating and we've chosen the correct of the two
  314.        possibilities.
  315.  
  316.        Useful: 3 Apr 94 765356300, 30 Oct 94 783496000 */
  317.  
  318.     if ((distance == -last_distance) && (distance < last_distance))
  319.       {
  320.         /* If the caller specified that the DST flag was off, it's
  321.                not possible to represent this time. */
  322.         if (me->tm_isdst == 0)
  323.           {
  324. #ifdef DEBUG
  325.         printf ("   Distance is oscillating -- dst flag nixes struct!\n");
  326. #endif
  327.         return BAD_STRUCT_TM;
  328.           }
  329.  
  330. #ifdef DEBUG
  331.         printf ("   Distance is oscillating -- chose the later time.\n");
  332. #endif
  333.         distance = 0;
  334.       }
  335.  
  336.     if ((distance == 0) && (me->tm_isdst != -1)
  337.         && (me->tm_isdst != guess_tm->tm_isdst))
  338.       {
  339.         /* If we're in this code, we've got the right time but the
  340.                wrong daylight savings flag.  We need to move away from
  341.                the time that we have and approach the other time from
  342.                the other direction.  That is, if I've requested the
  343.                non-DST version of a time and I get the DST version
  344.                instead, I want to put us forward in time and search
  345.                backwards to get the other time.  I checked all of the
  346.                configuration files for the tz package -- no entry
  347.                saves more than two hours, so I think we'll be safe by
  348.                moving 24 hours in one direction.  IF THE AMOUNT OF
  349.                TIME SAVED IN THE CONFIGURATION FILES CHANGES, THIS
  350.                VALUE MAY NEED TO BE ADJUSTED.  Luckily, we can never
  351.                have more than one level of overlaps, or this would
  352.                never work. */
  353.  
  354. #define SKIP_VALUE 86400
  355.  
  356.         if (guess_tm->tm_isdst == 0)
  357.           /* we got the later one, but want the earlier one */
  358.           distance = -SKIP_VALUE;
  359.         else
  360.           distance = SKIP_VALUE;
  361.         
  362. #ifdef DEBUG
  363.         printf ("   Got the right time, wrong DST value -- adjusting\n");
  364. #endif
  365.       }
  366.  
  367.     last_distance = distance;
  368.  
  369.       } while (distance != 0);
  370.  
  371.     /* Check to see that the dst flag matches */
  372.  
  373.     if (me->tm_isdst != -1)
  374.       {
  375.     if (me->tm_isdst != guess_tm->tm_isdst)
  376.       {
  377. #ifdef DEBUG
  378.         printf ("   DST flag doesn't match!  FIXME?\n");
  379. #endif
  380.         return BAD_STRUCT_TM;
  381.       }
  382.       }
  383.  
  384.     result = guess;        /* Success! */
  385.  
  386.     /* On successful completion, the values of tm_wday and tm_yday
  387.        have to be set appropriately. */
  388.     
  389.     /* me->tm_yday = guess_tm->tm_yday; 
  390.        me->tm_mday = guess_tm->tm_mday; */
  391.  
  392.     *me = *guess_tm;
  393.   }
  394.  
  395.   /* Update the caller's version of the structure */
  396.  
  397.   *timeptr = *me;
  398.  
  399.   return result;
  400. }
  401.  
  402. time_t
  403. #ifdef DEBUG            /* make it work even if the system's
  404.                    libc has it's own mktime routine */
  405. my_mktime (timeptr)
  406. #else
  407. mktime (timeptr)
  408. #endif
  409.      struct tm *timeptr;
  410. {
  411.   return _mktime_internal (timeptr, localtime);
  412. }
  413.  
  414. #ifdef DEBUG
  415. void
  416. main (argc, argv)
  417.      int argc;
  418.      char *argv[];
  419. {
  420.   int time;
  421.   int result_time;
  422.   struct tm *tmptr;
  423.   
  424.   if (argc == 1)
  425.     {
  426.       long q;
  427.       
  428.       printf ("starting long test...\n");
  429.  
  430.       for (q = 10000000; q < 1000000000; q += 599)
  431.     {
  432.       struct tm *tm = localtime ((time_t *) &q);
  433.       if ((q % 10000) == 0) { printf ("%ld\n", q); fflush (stdout); }
  434.       if (q != my_mktime (tm))
  435.         { printf ("failed for %ld\n", q); fflush (stdout); }
  436.     }
  437.       
  438.       printf ("test finished\n");
  439.  
  440.       exit (0);
  441.     }
  442.   
  443.   if (argc != 2)
  444.     {
  445.       printf ("wrong # of args\n");
  446.       exit (0);
  447.     }
  448.   
  449.   debugging_enabled = 1;    /* We want to see the info */
  450.  
  451.   ++argv;
  452.   time = atoi (*argv);
  453.   
  454.   tmptr = localtime ((time_t *) &time);
  455.   printf ("Localtime tells us that a time_t of %d represents\n     ", time);
  456.   printtm (tmptr);
  457.   putchar ('\n');
  458.  
  459.   printf ("   Given localtime's return val, mktime returns %d which is\n     ",
  460.       (int) my_mktime (tmptr));
  461.   printtm (tmptr);
  462.   putchar ('\n');
  463.  
  464. #if 0
  465.   tmptr->tm_sec -= 20;
  466.   tmptr->tm_min -= 20;
  467.   tmptr->tm_hour -= 20;
  468.   tmptr->tm_mday -= 20;
  469.   tmptr->tm_mon -= 20;
  470.   tmptr->tm_year -= 20;
  471.   tmptr->tm_gmtoff -= 20000;    /* This has no effect! */
  472.   tmptr->tm_zone = NULL;    /* Nor does this! */
  473.   tmptr->tm_isdst = -1;
  474. #endif
  475.   
  476.   tmptr->tm_hour += 1;
  477.   tmptr->tm_isdst = -1;
  478.  
  479.   printf ("\n\nchanged ranges: ");
  480.   printtm (tmptr);
  481.   putchar ('\n');
  482.  
  483.   result_time = my_mktime (tmptr);
  484.   printf ("\nmktime: %d\n", result_time);
  485.  
  486.   tmptr->tm_isdst = 0;
  487.  
  488.   printf ("\n\nchanged ranges: ");
  489.   printtm (tmptr);
  490.   putchar ('\n');
  491.  
  492.   result_time = my_mktime (tmptr);
  493.   printf ("\nmktime: %d\n", result_time);
  494. }
  495. #endif /* DEBUG */
  496.  
  497.  
  498. /*
  499. Local Variables:
  500. compile-command: "gcc -g mktime.c -o mktime -DDEBUG"
  501. End:
  502. */
  503.